Option Explicit

' ======================================================================
' Descriptor
' SQL 辅助 module
' ======================================================================

' ======================================================================
' SQL 对齐级别
' ======================================================================
Public Enum SqlAlignmentLevel
    ' 0 = 不做对齐
    NO_ALIGNMENT = 0

    ' 1 = 兼顾所有级别的条件组做对齐
    '     并使用 `multiNestAlignmentStrSizeDiff` 来限定对齐的阈值
    ALL_LEVEL = 1

    ' 2 = 默认级别= 只在各需要对齐的元素分组内部做对齐
    '     各级别的 sql 的 where 条件内部对象，join 内部条件对齐，不同级别之间不会进行对齐
    INTERNAL = 2

End Enum

' ======================================================================
' SQL 对齐位置
' ======================================================================
Public Enum SqlAlignmentPosition
    ' 在运算符的右侧对齐
    OPERATOR_RIGHT = 0

    ' 在运算符的左侧对齐
    OPERATOR_LEFT = 1

End Enum

' ' ======================================================================
' ' SQL 组件 类型枚举
' ' ======================================================================
' Public Enum SqlComponentType
'     ' 未知类型
'     UNKNOWN = 0

'     '-----------------------
'     ' 列
'     '-----------------------
'     ' 真实列
'     COL_REAL = 1
'     ' 字面量列
'     COL_LITERAL = 2
'     ' 全数据列
'     COL_ASTERISK = 3
'     ' 子查询列
'     COL_SUB_QUERY = 4
'     ' 函数列
'     COL_FUNC = 5
'     ' case 列
'     COL_CASE = 6
'     ' 表达式列
'     COL_EXPR = 7
'     ' 条件列
'     COL_CONDITION = 8
' End Enum

' ======================================================================
' SQL token 类型枚举
' ======================================================================
Public Enum SqlTokenType
    ' 无意义内容
    NO_MEAN = 0

    ' ==================================================
    ' sql 自定义声明部分
    ' ==================================================
    ' 函数名称
    STR_FUNC = 1
    ' 字符串名称
    STR_NAME = 2
    ' 数字
    STR_NUM = 3
    ' 由 '' 包裹的普通字符串
    STR_STR = 4
    ' 换行符，同时代表 CrLf + Lf 两种换行符
    STR_LINE_BREAK = 5

    ' ==================================================
    ' sql 基础符号
    ' ==================================================
    ' * 星号 （和乘号不同）
    ASTERISK = 21
    ' [.]
    POINT = 22
    ' 左括号 (
    LEFT_PARENTHESIS = 23
    ' 右括号 )
    RIGHT_PARENTHESIS = 24
    ' [,]
    COMMA = 25
    ' ;
    SEMICOLON = 26

    ' ==================================================
    ' 运算符部分
    ' ==================================================
    ' 字符串运算符 ||
    OPERA_STR_CONCAT = 41
    ' 算数运算符
    ' +
    OPERA_PLUS = 42
    ' -
    OPERA_MINUS = 43
    ' *
    OPERA_MULTI = 44
    ' /
    OPERA_DIVISION = 45
    ' 比较运算符
    ' =
    OPERA_EQ = 46
    ' !=
    OPERA_NE = 47
    ' <
    OPERA_LT = 48
    ' >
    OPERA_GT = 49
    ' <=
    OPERA_LE = 50
    ' >=
    OPERA_GE = 51

    ' ==================================================
    ' sql 关键字部分
    ' ==================================================
    KEY_SELECT = 101
    KEY_FROM = 102
    KEY_INSERT = 103
    KEY_INTO = 104
    KEY_INNER = 105
    KEY_CROSS = 106
    KEY_JOIN = 107
    KEY_OUTER = 108
    KEY_LEFT = 109
    KEY_RIGHT = 110
    KEY_ON = 111
    KEY_UPDATE = 112
    KEY_DELETE = 113
    KEY_EXISTS = 114
    KEY_NOT = 115
    KEY_LIKE = 116
    KEY_ORDER_BY = 117
    KEY_GROUP_BY = 119
    KEY_HAVING = 120
    KEY_NULL = 121
    KEY_VALUES = 122
    KEY_VALUE = 123
    KEY_SET = 124
    KEY_WHERE = 125
    KEY_AND = 126
    KEY_OR = 127
    KEY_BETWEEN = 128
    KEY_AS = 129
    KEY_LIMIT = 130
    KEY_OFFSET = 131
    KEY_UNION = 132
    KEY_TRUNCATE = 135
    KEY_CREATE = 136
    KEY_TABLE = 137
    KEY_IN = 138
    KEY_WITH = 139
    KEY_ASC = 140
    KEY_DESC = 141
    KEY_CASE = 142
    KEY_IS = 143
    KEY_BINARY = 144
    KEY_PARTITION = 145
    KEY_PROCEDURE = 146 ' procedure
    KEY_WHEN = 147
    KEY_END = 148
    KEY_THEN = 149
    KEY_ELSE = 150
    KEY_CAST = 151
    KEY_ALTER = 152
    ' boolean 关键字 - True
    KEY_TRUE = 153
    ' boolean 关键字 - False
    KEY_FALSE = 154
    KEY_ANY = 155
    KEY_ROLLUP = 156

    '---------------------------
    ' select 开头的行为关键字
    KEY_DISTINCTROW = 157
    KEY_ALL = 158 ' ----- all 在这里既可以作为select行为关键字，也可以作为 union all 的关键字。不过为了判断方便，记录到这里
    KEY_DISTINCT = 159
    KEY_HIGH_PRIORITY = 160
    KEY_STRAIGHT_JOIN = 161
    KEY_SQL_SMALL_RESULT = 162
    KEY_SQL_BIG_RESULT = 163
    KEY_SQL_BUFFER_RESULT = 164
    KEY_SQL_CACHE = 165
    KEY_SQL_NO_CACHE = 166
    KEY_SQL_CALC_FOUND_ROWS = 167

    ' update 开头的行为关键字
    KEY_LOW_PRIORITY = 168
    KEY_IGNORE = 169

    ' insert 开头的行为关键字
    KEY_DELAYED = 170

    ' delete 开头的行为关键字
    KEY_QUICK = 171
    '---------------------------

    'index_hint
    ' index_hint -> USE {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] ([index_list])
    '             | {IGNORE|FORCE} {INDEX|KEY} [FOR {JOIN|ORDER BY|GROUP BY}] (index_list)
    KEY_USE = 172
    KEY_INDEX = 173
    KEY_KEY = 174
    KEY_FOR = 175
    KEY_FORCE = 176

    ' delete
    KEY_USING = 177

    'insert
    KEY_DUPLICATE = 178

    ' with
    KEY_RECURSIVE = 179

    ' ==================================================
    ' sql 常量
    ' ==================================================
    ' 时间戳 CURRENT_TIMESTAMP
    CONST_CURRENT_TIMESTAMP = 301

    ' ==================================================
    ' sql 注释
    ' ==================================================
    ' sql 注释 (和前一个元素在同一行)
    SHARE_LINE_COMMENT = 998
    ' sql 注释 (单独一行的注释。这一行上只有这一行注释，没有其他的元素)
    SINGLE_LINE_COMMENT = 999 
End Enum

' ======================================================================
' 相关常量
' ======================================================================
' =======================================
' SQL token 类型枚举辅助方法 需要使用的缓存
' =======================================
' sql 关键字 --> sql 关键字枚举值的映射
' Map<String, SqlTokenType>
Private sqlKeyTokenTypeMap As Object

' 使匹配筛选项操作停止的 sql 关键字集合
' 参考:
'    https://dev.mysql.com/doc/refman/5.7/en/select.html
' 包含的关键字
' and, or, where, join, from, left, right, inner, cross, partition
' group, by, order, on, limit, having, offset, procedure
Private selectItemStopSqlKey As ISet

' 各部分开始的 sql 关键字
' 包含的关键字
'    LEFT, RIGHT, OUTER, JOIN, CROSS
'    WITH, FROM, WHERE, GROUP_BY
'    HAVING, ORDER_BY, LIMIT, OFFSET
'    SELECT, INSERT, UPDATE, DELETE, TRUNCATE
Private sectionStartSqlKeySet As ISet

' =======================================
' 有意义分隔符映射 需要使用的缓存
' =======================================
' 有意义分隔符映射
' Map<String, SqlTokenType>
Private meaningfulDelimiterMap As Object

' ======================================================================
' SQL token 类型枚举辅助方法
' ======================================================================

' -----------------------------------
' 处理缓存: sqlKeyTokenTypeMap
' -----------------------------------

' 获取 sql 关键字 <--> sql 关键字枚举的映射
Public Function getSqlKeyTokenTypeMap() As Object
    If sqlKeyTokenTypeMap Is Nothing Then
        createSqlKeyTokenTypeMap
    End If

    Set getSqlKeyTokenTypeMap = sqlKeyTokenTypeMap
End Function

' 创建: sql 关键字 --> sql 关键字枚举值的映射
Private Sub createSqlKeyTokenTypeMap()
    Set sqlKeyTokenTypeMap = UtilsCreateSysObj.createMap

    sqlKeyTokenTypeMap.Add "select", SqlTokenType.KEY_SELECT
    sqlKeyTokenTypeMap.Add "from", SqlTokenType.KEY_FROM
    sqlKeyTokenTypeMap.Add "insert", SqlTokenType.KEY_INSERT
    sqlKeyTokenTypeMap.Add "into", SqlTokenType.KEY_INTO
    sqlKeyTokenTypeMap.Add "inner", SqlTokenType.KEY_INNER
    sqlKeyTokenTypeMap.Add "cross", SqlTokenType.KEY_CROSS
    sqlKeyTokenTypeMap.Add "join", SqlTokenType.KEY_JOIN
    sqlKeyTokenTypeMap.Add "outer", SqlTokenType.KEY_OUTER
    sqlKeyTokenTypeMap.Add "left", SqlTokenType.KEY_LEFT
    sqlKeyTokenTypeMap.Add "right", SqlTokenType.KEY_RIGHT
    sqlKeyTokenTypeMap.Add "update", SqlTokenType.KEY_UPDATE
    sqlKeyTokenTypeMap.Add "delete", SqlTokenType.KEY_DELETE
    sqlKeyTokenTypeMap.Add "on", SqlTokenType.KEY_ON
    sqlKeyTokenTypeMap.Add "exists", SqlTokenType.KEY_EXISTS
    sqlKeyTokenTypeMap.Add "not", SqlTokenType.KEY_NOT
    sqlKeyTokenTypeMap.Add "like", SqlTokenType.KEY_LIKE
    'sqlKeyTokenTypeMap.Add "order", SqlTokenType.KEY_ORDER_BY
    'sqlKeyTokenTypeMap.Add "group", SqlTokenType.KEY_GROUP_BY
    sqlKeyTokenTypeMap.Add "having", SqlTokenType.KEY_HAVING
    sqlKeyTokenTypeMap.Add "null", SqlTokenType.KEY_NULL
    sqlKeyTokenTypeMap.Add "values", SqlTokenType.KEY_VALUES
    sqlKeyTokenTypeMap.Add "value", SqlTokenType.KEY_VALUE
    sqlKeyTokenTypeMap.Add "set", SqlTokenType.KEY_SET
    sqlKeyTokenTypeMap.Add "where", SqlTokenType.KEY_WHERE
    sqlKeyTokenTypeMap.Add "and", SqlTokenType.KEY_AND
    sqlKeyTokenTypeMap.Add "or", SqlTokenType.KEY_OR
    sqlKeyTokenTypeMap.Add "between", SqlTokenType.KEY_BETWEEN
    sqlKeyTokenTypeMap.Add "as", SqlTokenType.KEY_AS
    sqlKeyTokenTypeMap.Add "limit", SqlTokenType.KEY_LIMIT
    sqlKeyTokenTypeMap.Add "offset", SqlTokenType.KEY_OFFSET
    sqlKeyTokenTypeMap.Add "union", SqlTokenType.KEY_UNION
    sqlKeyTokenTypeMap.Add "truncate", SqlTokenType.KEY_TRUNCATE
    sqlKeyTokenTypeMap.Add "create", SqlTokenType.KEY_CREATE
    sqlKeyTokenTypeMap.Add "table", SqlTokenType.KEY_TABLE
    sqlKeyTokenTypeMap.Add "in", SqlTokenType.KEY_IN
    sqlKeyTokenTypeMap.Add "with", SqlTokenType.KEY_WITH
    sqlKeyTokenTypeMap.Add "asc", SqlTokenType.KEY_ASC
    sqlKeyTokenTypeMap.Add "desc", SqlTokenType.KEY_DESC
    sqlKeyTokenTypeMap.Add "case", SqlTokenType.KEY_CASE
    sqlKeyTokenTypeMap.Add "is", SqlTokenType.KEY_IS
    sqlKeyTokenTypeMap.Add "binary", SqlTokenType.KEY_BINARY
    sqlKeyTokenTypeMap.Add "partition", SqlTokenType.KEY_PARTITION
    sqlKeyTokenTypeMap.Add "when", SqlTokenType.KEY_WHEN
    sqlKeyTokenTypeMap.Add "end", SqlTokenType.KEY_END
    sqlKeyTokenTypeMap.Add "then", SqlTokenType.KEY_THEN
    sqlKeyTokenTypeMap.Add "else", SqlTokenType.KEY_ELSE
    sqlKeyTokenTypeMap.Add "cast", SqlTokenType.KEY_CAST
    sqlKeyTokenTypeMap.Add "alter", SqlTokenType.KEY_ALTER
    sqlKeyTokenTypeMap.Add "true", SqlTokenType.KEY_TRUE
    sqlKeyTokenTypeMap.Add "false", SqlTokenType.KEY_FALSE
    sqlKeyTokenTypeMap.Add "any", SqlTokenType.KEY_ANY
    sqlKeyTokenTypeMap.Add "rollup", SqlTokenType.KEY_ROLLUP

    ' update 开头的关键字
    sqlKeyTokenTypeMap.Add "low_priority", SqlTokenType.KEY_LOW_PRIORITY
    sqlKeyTokenTypeMap.Add "ignore", SqlTokenType.KEY_IGNORE

    'index_hint
    sqlKeyTokenTypeMap.Add "use", SqlTokenType.KEY_USE
    sqlKeyTokenTypeMap.Add "index", SqlTokenType.KEY_INDEX
    sqlKeyTokenTypeMap.Add "key", SqlTokenType.KEY_KEY
    sqlKeyTokenTypeMap.Add "for", SqlTokenType.KEY_FOR
    sqlKeyTokenTypeMap.Add "force", SqlTokenType.KEY_FORCE

    ' delete
    sqlKeyTokenTypeMap.Add "quick", SqlTokenType.KEY_QUICK
    sqlKeyTokenTypeMap.Add "using", SqlTokenType.KEY_USING

    'insert
    sqlKeyTokenTypeMap.Add "delayed", SqlTokenType.KEY_DELAYED
    sqlKeyTokenTypeMap.Add "duplicate", SqlTokenType.KEY_DUPLICATE

    ' select 开头的部分关键字
    sqlKeyTokenTypeMap.Add "distinctrow", SqlTokenType.KEY_DISTINCTROW
    sqlKeyTokenTypeMap.Add "distinct", SqlTokenType.KEY_DISTINCT
    sqlKeyTokenTypeMap.Add "all", SqlTokenType.KEY_ALL
    sqlKeyTokenTypeMap.Add "high_priority", SqlTokenType.KEY_HIGH_PRIORITY
    sqlKeyTokenTypeMap.Add "straight_join", SqlTokenType.KEY_STRAIGHT_JOIN
    sqlKeyTokenTypeMap.Add "sql_small_result", SqlTokenType.KEY_SQL_SMALL_RESULT
    sqlKeyTokenTypeMap.Add "sql_big_result", SqlTokenType.KEY_SQL_BIG_RESULT
    sqlKeyTokenTypeMap.Add "sql_buffer_result", SqlTokenType.KEY_SQL_BUFFER_RESULT
    sqlKeyTokenTypeMap.Add "sql_cache", SqlTokenType.KEY_SQL_CACHE
    sqlKeyTokenTypeMap.Add "sql_no_cache", SqlTokenType.KEY_SQL_NO_CACHE
    sqlKeyTokenTypeMap.Add "sql_calc_found_rows", SqlTokenType.KEY_SQL_CALC_FOUND_ROWS

    ' with
    sqlKeyTokenTypeMap.Add "recursive", SqlTokenType.KEY_RECURSIVE
End Sub

' -----------------------------------
' 处理缓存: selectItemStopSqlKey
' -----------------------------------

' 创建: 使匹配筛选项操作停止的 sql 关键字集合
Private Sub createSelectItemStopSqlKey()
    Set selectItemStopSqlKey = UtilsSet.createHashSet

    ' and
    selectItemStopSqlKey.Add SqlTokenType.KEY_AND
    ' or
    selectItemStopSqlKey.Add SqlTokenType.KEY_OR
    ' where
    selectItemStopSqlKey.Add SqlTokenType.KEY_WHERE
    ' join
    selectItemStopSqlKey.Add SqlTokenType.KEY_JOIN
    ' from
    selectItemStopSqlKey.Add SqlTokenType.KEY_FROM
    ' left
    selectItemStopSqlKey.Add SqlTokenType.KEY_LEFT
    ' right
    selectItemStopSqlKey.Add SqlTokenType.KEY_RIGHT
    ' inner
    selectItemStopSqlKey.Add SqlTokenType.KEY_INNER
    ' cross
    selectItemStopSqlKey.Add SqlTokenType.KEY_CROSS
    ' partition
    selectItemStopSqlKey.Add SqlTokenType.KEY_PARTITION
    ' group by
    selectItemStopSqlKey.Add SqlTokenType.KEY_GROUP_BY
    ' order by
    selectItemStopSqlKey.Add SqlTokenType.KEY_ORDER_BY
    ' on
    selectItemStopSqlKey.Add SqlTokenType.KEY_ON
    ' limit
    selectItemStopSqlKey.Add SqlTokenType.KEY_LIMIT
    ' having
    selectItemStopSqlKey.Add SqlTokenType.KEY_HAVING
    ' offset
    selectItemStopSqlKey.Add SqlTokenType.KEY_OFFSET
    ' procedure
    selectItemStopSqlKey.Add SqlTokenType.KEY_PROCEDURE
    ' union
    selectItemStopSqlKey.Add SqlTokenType.KEY_UNION
End Sub

' -----------------------------------
' 处理缓存: sectionStartSqlKeySet
' -----------------------------------

' 创建: 各部分开始的 sql 关键字
Private Sub createSectionStartSqlKeySet()
    Set sectionStartSqlKeySet = UtilsSet.createHashSet

    sectionStartSqlKeySet.Add SqlTokenType.KEY_LEFT
    sectionStartSqlKeySet.Add SqlTokenType.KEY_RIGHT
    sectionStartSqlKeySet.Add SqlTokenType.KEY_OUTER
    sectionStartSqlKeySet.Add SqlTokenType.KEY_JOIN
    sectionStartSqlKeySet.Add SqlTokenType.KEY_CROSS
    sectionStartSqlKeySet.Add SqlTokenType.KEY_WITH
    sectionStartSqlKeySet.Add SqlTokenType.KEY_FROM
    sectionStartSqlKeySet.Add SqlTokenType.KEY_WHERE
    sectionStartSqlKeySet.Add SqlTokenType.KEY_GROUP_BY
    sectionStartSqlKeySet.Add SqlTokenType.KEY_HAVING
    sectionStartSqlKeySet.Add SqlTokenType.KEY_ORDER_BY
    sectionStartSqlKeySet.Add SqlTokenType.KEY_LIMIT
    sectionStartSqlKeySet.Add SqlTokenType.KEY_OFFSET
    sectionStartSqlKeySet.Add SqlTokenType.KEY_SELECT
    sectionStartSqlKeySet.Add SqlTokenType.KEY_INSERT
    sectionStartSqlKeySet.Add SqlTokenType.KEY_UPDATE
    sectionStartSqlKeySet.Add SqlTokenType.KEY_DELETE
    sectionStartSqlKeySet.Add SqlTokenType.KEY_TRUNCATE
End Sub

' =======================================
' SqlTokenType 的匹配操作
' =======================================

' 检查参数字符串是否是一个 sql 关键字
Public Function isSqlKey(ByRef k As String) As Boolean
    If sqlKeyTokenTypeMap Is Nothing Then
        createSqlKeyTokenTypeMap
    End If

    isSqlKey = sqlKeyTokenTypeMap.Exists(k)
End Function

' 通过字符串获取对应的 token 类型枚举
'
' 只处理小写字符，所以检查前需要将输入参数主动转换成小写
'
' 如果无法找到相匹配的关键字，将会返回 SqlTokenType.NO_MEAN
'
' @param key sql关键字字符串
Public Function getSqlKeyTokenType(ByRef key As String) As SqlTokenType
    If sqlKeyTokenTypeMap Is Nothing Then
        createSqlKeyTokenTypeMap
    End If

    If sqlKeyTokenTypeMap.Exists(key) Then
        getSqlKeyTokenType = sqlKeyTokenTypeMap.Item(key)
    Else
        getSqlKeyTokenType = SqlTokenType.NO_MEAN
    End If
End Function

' 检查 tokenType 是不是比较运算符
Public Function tokenTypeIsCompareOperator(ByRef t As SqlTokenType) As Boolean
    tokenTypeIsCompareOperator = (t >= SqlTokenType.OPERA_EQ And t <= SqlTokenType.OPERA_GE)
End Function

' 检查 tokenType 是不是条件运算符 (比较运算符 + 其他)
' 除了基础的比较运算符，还包括: `between`, `in`, `exists`, `like`, 'is'
'
' @param t 需要检查的 token 类型
Public Function tokenTypeIsConditionOperator(ByRef t As SqlTokenType) As Boolean
    If t >= SqlTokenType.OPERA_EQ And t <= SqlTokenType.OPERA_GE Then
        tokenTypeIsConditionOperator = True
    ElseIf t = SqlTokenType.KEY_BETWEEN Then
        tokenTypeIsConditionOperator = True
    ElseIf t = SqlTokenType.KEY_IN Then
        tokenTypeIsConditionOperator = True
    ElseIf t = SqlTokenType.KEY_EXISTS Then
        tokenTypeIsConditionOperator = True
    ElseIf t = SqlTokenType.KEY_LIKE Then
        tokenTypeIsConditionOperator = True
    ElseIf t = SqlTokenType.KEY_IS Then
        tokenTypeIsConditionOperator = True
    Else
        tokenTypeIsConditionOperator = False
    End If
End Function

' 检查 `SqlTokenType` 枚举类型是不是 sql 关键字
' @param t 需要检查的 token 类型
Public Function isSqlKeyTokenType(ByRef t As SqlTokenType) As Boolean
    If t >= SqlTokenType.KEY_SELECT And t <= SqlTokenType.KEY_SQL_CALC_FOUND_ROWS Then
        isSqlKeyTokenType = True
    Else
        isSqlKeyTokenType = False
    End If
End Function

' 检查 `SqlTokenType` 枚举类型是不是逻辑运算符
' @param t 需要检查的 token 类型
Public Function tokenTypeIsLogicOperator(ByRef t As SqlTokenType) As Boolean
    If t = SqlTokenType.KEY_AND Then
        tokenTypeIsLogicOperator = True
    ElseIf t = SqlTokenType.KEY_OR Then
        tokenTypeIsLogicOperator = True
    Else
        tokenTypeIsLogicOperator = False
    End If
End Function

' 检查 `SqlTokenType` 枚举类型是不是算数运算符
' 包括: `||`, `"+"`, `"-"`, `"*"`, `"/"`
'
' @param t 需要检查的 token 类型
Public Function tokenTypeIsComputeOperator(ByRef t As SqlTokenType) As Boolean
    If t >= SqlTokenType.OPERA_STR_CONCAT And t <= SqlTokenType.OPERA_DIVISION Then
        tokenTypeIsComputeOperator = True
    Else
        tokenTypeIsComputeOperator = False
    End If
End Function

' 检查 `SqlTokenType` 枚举类型是不是 `join` 开始时的 sql 关键字
' 包括: `left`, `right`, `inner`, `join`, `cross`
'
' @param t 需要检查的 token 类型
Public Function tokenTypeIsJoinStartSqlKey(ByRef t As SqlTokenType) As Boolean
    If t = SqlTokenType.KEY_LEFT Then
        tokenTypeIsJoinStartSqlKey = True
    ElseIf t = SqlTokenType.KEY_RIGHT Then
        tokenTypeIsJoinStartSqlKey = True
    ElseIf t = SqlTokenType.KEY_INNER Then
        tokenTypeIsJoinStartSqlKey = True
    ElseIf t = SqlTokenType.KEY_JOIN Then
        tokenTypeIsJoinStartSqlKey = True
    ElseIf t = SqlTokenType.KEY_CROSS Then
        tokenTypeIsJoinStartSqlKey = True
    Else
        tokenTypeIsJoinStartSqlKey = False
    End If
End Function

' 检查 `SqlTokenType` 枚举类型是不是使匹配筛选项操作停止的 sql 关键字集合
' @param t 需要检查的 token 类型
Public Function tokenTypeIsSelectItemStopSqlKey(ByRef t As SqlTokenType) As Boolean
    If selectItemStopSqlKey Is Nothing Then
        createSelectItemStopSqlKey
    End If

    tokenTypeIsSelectItemStopSqlKey = selectItemStopSqlKey.Exists(t)
End Function

' 检查 `SqlTokenType` 枚举类型是不是筛选项中的筛选行为关键字
' 参考
'   https://dev.mysql.com/doc/refman/5.7/en/select.html
'
' 包含的关键字
'   1. 会在 select 开头部分出现的关键字
'     DISTINCT, DISTINCTROW, HIGH_PRIORITY,
'     STRAIGHT_JOIN, SQL_SMALL_RESULT, SQL_BIG_RESULT,
'     SQL_BUFFER_RESULT, SQL_CACHE, SQL_NO_CACHE,
'     SQL_CALC_FOUND_ROWS
'   2. 其他可能出现的关键字
'     BINARY, NOT, ALL
'
' @param t 需要检查的 token 类型
Public Function tokenTypeIsInSelectStartActionSqlKey(ByRef t As SqlTokenType) As Boolean
    If t >= SqlTokenType.KEY_DISTINCTROW And t <= SqlTokenType.KEY_SQL_CALC_FOUND_ROWS Then
        tokenTypeIsInSelectStartActionSqlKey = True
    'ElseIf t >= SqlTokenType.KEY_BINARY Then
    '    tokenTypeIsInSelectStartActionSqlKey = True
    'ElseIf t >= SqlTokenType.KEY_NOT Then
    '    tokenTypeIsInSelectStartActionSqlKey = True
    'ElseIf t >= SqlTokenType.KEY_ALL Then
    '    tokenTypeIsInSelectStartActionSqlKey = True
    Else
        tokenTypeIsInSelectStartActionSqlKey = False
    End If
End Function

' 检查 `SqlTokenType` 枚举类型是不是各部分开始的关键字
Public Function tokenTypeIsSectionStartSqlKey(ByRef t As SqlTokenType) As Boolean
    If sectionStartSqlKeySet Is Nothing Then
        createSectionStartSqlKeySet
    End If

    tokenTypeIsSectionStartSqlKey = sectionStartSqlKeySet.Exists(t)
End Function

' 检查 `SqlTokenType` 枚举类型是不是 index_hint 的起始关键字
' @param s 需要检查的字符串
Public Function isIndexHintStartKey(ByRef t As SqlTokenType) As Boolean
    If t = SqlTokenType.KEY_USE Then
        isIndexHintStartKey = True
    ElseIf t = SqlTokenType.KEY_IGNORE Then
        isIndexHintStartKey = True
    ElseIf t = SqlTokenType.KEY_FORCE Then
        isIndexHintStartKey = True
    Else
        isIndexHintStartKey = False
    End If
End Function

' 检查 `SqlTokenType` 枚举类型是不是各类型语句中的行为关键字
' @param t 需要检查的 token 类型
Public Function tokenTypeIsInSqlStartActionSqlKey(ByRef t As SqlTokenType) As Boolean
    If t >= SqlTokenType.KEY_DISTINCTROW And t <= SqlTokenType.KEY_QUICK Then
        tokenTypeIsInSqlStartActionSqlKey = True
    Else
        tokenTypeIsInSqlStartActionSqlKey = False
    End If
End Function

' 检查 `SqlTokenType` 枚举类型是不是各类型的运算符
' @param t 需要检查的 token 类型
Public Function tokenTypeIsOperator(ByRef t As SqlTokenType) As Boolean
    ' 检查: 如果是算数运算符，则返回 true
    If t >= SqlTokenType.OPERA_STR_CONCAT And t <= SqlTokenType.OPERA_DIVISION Then
        tokenTypeIsOperator = True
    ' 检查: 如果是逻辑运算符，则返回 true
    ElseIf t = SqlTokenType.KEY_AND Then
        tokenTypeIsOperator = True
    ElseIf t = SqlTokenType.KEY_OR Then
        tokenTypeIsOperator = True
    ' 检查: 如果是条件运算符，则返回 true
    ElseIf t >= SqlTokenType.OPERA_EQ And t <= SqlTokenType.OPERA_GE Then
        tokenTypeIsOperator = True
    ElseIf t = SqlTokenType.KEY_BETWEEN Then
        tokenTypeIsOperator = True
    ElseIf t = SqlTokenType.KEY_IN Then
        tokenTypeIsOperator = True
    ElseIf t = SqlTokenType.KEY_EXISTS Then
        tokenTypeIsOperator = True
    ElseIf t = SqlTokenType.KEY_LIKE Then
        tokenTypeIsOperator = True
    ElseIf t = SqlTokenType.KEY_IS Then
        tokenTypeIsOperator = True
    ElseIf t = SqlTokenType.KEY_NOT Then
        tokenTypeIsOperator = True
    End If
End Function

' ======================================================================
' 处理 sql 常量
' ======================================================================
' 检查 `SqlTokenType` 枚举类型是不是 sql 中的常量
Public Function tokenTypeIsSqlConst(ByRef t As SqlTokenType) As Boolean
    tokenTypeIsSqlConst = (t = SqlTokenType.CONST_CURRENT_TIMESTAMP)
End Function

' 检查参数字符串是否是 sql 中的常量
'
' 注意: 只处理小写字符
Public Function isSqlConst(ByRef k As String) As Boolean
    isSqlConst = (k = "current_timestamp")
End Function

' 获取 sql 常量的 SqlTokenType
'
' 注意: 只处理小写字符
Public Function getSqlConstType(ByRef k As String) As SqlTokenType
    If k = "current_timestamp" Then
        getSqlConstType = SqlTokenType.CONST_CURRENT_TIMESTAMP
    Else
        getSqlConstType = SqlTokenType.NO_MEAN
    End If
End Function

' ======================================================================
' 有意义的 sql 分隔符
' ======================================================================

' 创建: 有意义分隔符映射
Private Sub createStrSetMeaningfulDelimiterMap()
    Set meaningfulDelimiterMap = UtilsCreateSysObj.createMap

        ' 基础分割符
        meaningfulDelimiterMap.Add "(", SqlTokenType.LEFT_PARENTHESIS
        meaningfulDelimiterMap.Add ")", SqlTokenType.RIGHT_PARENTHESIS
        meaningfulDelimiterMap.Add ".", SqlTokenType.POINT
        meaningfulDelimiterMap.Add ",", SqlTokenType.COMMA
        meaningfulDelimiterMap.Add ";", SqlTokenType.SEMICOLON
        ' 运算符
        meaningfulDelimiterMap.Add "||", SqlTokenType.OPERA_STR_CONCAT
        meaningfulDelimiterMap.Add "+", SqlTokenType.OPERA_PLUS
        meaningfulDelimiterMap.Add "-", SqlTokenType.OPERA_MINUS
        meaningfulDelimiterMap.Add "*", SqlTokenType.OPERA_MULTI
        meaningfulDelimiterMap.Add "/", SqlTokenType.OPERA_DIVISION
        meaningfulDelimiterMap.Add "=", SqlTokenType.OPERA_EQ
        meaningfulDelimiterMap.Add "!=", SqlTokenType.OPERA_NE
        meaningfulDelimiterMap.Add "<>", SqlTokenType.OPERA_NE
        meaningfulDelimiterMap.Add "<", SqlTokenType.OPERA_LT
        meaningfulDelimiterMap.Add "&lt;", SqlTokenType.OPERA_LT
        meaningfulDelimiterMap.Add ">", SqlTokenType.OPERA_GT
        meaningfulDelimiterMap.Add "&gt;", SqlTokenType.OPERA_GT
        meaningfulDelimiterMap.Add "<=", SqlTokenType.OPERA_LE
        meaningfulDelimiterMap.Add "&lt;=", SqlTokenType.OPERA_LE
        meaningfulDelimiterMap.Add ">=", SqlTokenType.OPERA_GE
        meaningfulDelimiterMap.Add "&gt;=", SqlTokenType.OPERA_GE
End Sub

' 获取创建有意义分隔符集合的枚举值
Public Function getTokenTypeForMeaningfulDelimiter(ByRef s As String) As SqlTokenType
    If meaningfulDelimiterMap Is Nothing Then
        createStrSetMeaningfulDelimiterMap
    End If

    If meaningfulDelimiterMap.Exists(s) Then
        getTokenTypeForMeaningfulDelimiter = meaningfulDelimiterMap.Item(s)
    Else
        getTokenTypeForMeaningfulDelimiter = SqlTokenType.NO_MEAN
    End If
End Function

' 检查字符串 s 是不是有意义的 sql 分隔符
' @param s 需要检查的字符串
Public Function isMeaningfulDelimiterStr(ByRef s As String) As Boolean
    If meaningfulDelimiterMap Is Nothing Then
        createStrSetMeaningfulDelimiterMap
    End If

    isMeaningfulDelimiterStr = meaningfulDelimiterMap.Exists(s)
End Function

' ======================================================================
' SQL 的其他操作
' ======================================================================

' 检查字符串 s 是不是 sql 中的无意义字符
' 包含
'    " "
'    "\t"
'    "\r\n"
'    "\n"
'    "\r"
Public Function isSqlNoMeanStr(ByRef s As String) As Boolean
    If s = " " Then
        isSqlNoMeanStr = True
    ElseIf s = vbTab Then
        isSqlNoMeanStr = True
    ElseIf s = vbCrLf Then
        isSqlNoMeanStr = True
    ElseIf s = vbLf Then
        isSqlNoMeanStr = True
    ElseIf s = vbCr Then
        isSqlNoMeanStr = True
    Else
        isSqlNoMeanStr = False
    End If
End Function

' 检查 token 类型是不是注释
Public Function isCommentToken(ByRef tokenType As SqlTokenType) As Boolean
    If tokenType = SqlTokenType.SHARE_LINE_COMMENT Then
        isCommentToken = True
    ElseIf tokenType = SqlTokenType.SINGLE_LINE_COMMENT Then
        isCommentToken = True
    Else
        isCommentToken = False
    End If
End Function